package com.kaibes.web.prefix;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler;

import com.kaibes.core.base.util.StringUtils;

@Component
public class PrefixHandler extends ViewNameMethodReturnValueHandler {

    @Autowired
    private PrefixContext context;
    @Autowired
    private PrefixConfig uiConfig;
    
    private Map<String, String> viewMap = new HashMap<>();

    protected void handlePrefix(String prefix, String name, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        super.handleReturnValue(new StringBuilder(prefix).append("/").append(name).toString(), returnType, mavContainer,
                webRequest);
    }
    
    public void replace(String oldView, String newView) {
        viewMap.put(oldView, newView);
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        Annotation[] annotations = returnType.getMethod().getDeclaringClass().getAnnotations();
        boolean flag = false;
        for (Annotation annotation : annotations) {
            if (annotation.annotationType() == PrefixController.class
                    || annotation.annotationType().isAnnotationPresent(PrefixController.class)) {
                flag = true;
                break;
            }
        }
        return (flag && CharSequence.class.isAssignableFrom(returnType.getParameterType()));
    }

    @Override
    public final void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        if (returnValue instanceof String) {
            String name = (String) returnValue;
            if (StringUtils.isNotBlank(name)) {
                String newView = viewMap.get(name);
                if (StringUtils.isNotBlank(newView)) {
                    name = newView;
                }
            }
            
            if (StringUtils.isBlank(name)) {
                super.handleReturnValue("redirect:/404", returnType, mavContainer, webRequest);
            } else if (StringUtils.startsWith(name, "redirect", "forward", "classpath:", "file:")) {
                super.handleReturnValue(name, returnType, mavContainer, webRequest);
            } else if (name.startsWith("!:")) {
                super.handleReturnValue(name.substring(2), returnType, mavContainer, webRequest);
            } else {
                String uri = webRequest.getNativeRequest(HttpServletRequest.class).getRequestURI();
                PrefixTarget prefixTarget = context.findPrefixTargetByUri(uri);
                if (prefixTarget != null) {
                    if (uiConfig.isLanguaged()) {
                        String language = webRequest.getNativeRequest(HttpServletRequest.class).getHeader("accept-language").split(",")[0];
                        handlePrefix(prefixTarget.getTarget()+"/"+language, name, returnType, mavContainer, webRequest);
                    } else {
                        handlePrefix(prefixTarget.getTarget(), name, returnType, mavContainer, webRequest);
                    }
                    
                } else {
                    super.handleReturnValue(name, returnType, mavContainer, webRequest);
                }
            }
            return;
        }
        super.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }

    public void init(RequestMappingHandlerAdapter adapter) {
        final List<HandlerMethodReturnValueHandler> originalHandlers = new ArrayList<>(
                adapter.getReturnValueHandlers());
        int position = obtainValueHandlerPosition(originalHandlers, getClass());
        if (position != -1) {
            originalHandlers.add(position, this);
        }
        adapter.setReturnValueHandlers(originalHandlers);
    }

    private int obtainValueHandlerPosition(final List<HandlerMethodReturnValueHandler> originalHandlers,
            Class<?> handlerClass) {
        for (int i = 0; i < originalHandlers.size(); i++) {
            final HandlerMethodReturnValueHandler valueHandler = originalHandlers.get(i);
            if (valueHandler.getClass().isAssignableFrom(handlerClass)) {
                return i;
            }
        }
        return -1;
    }
}
